<template>
	<view class="lsj-file" :class="[webId]" :style="[getStyles]">
		<view ref="lsj" class="hFile" :style="[getStyles]" @click="onClick">
			<slot><view class="defview" :style="[getStyles]">附件上传</view></slot>
		</view>
	</view>
</template>

<script>
// 查看文档：https://ext.dcloud.net.cn/plugin?id=5459
import {LsjFile} from './LsjFile.js' 
let observer = null;
export default {
	name: 'Lsj-upload',
	props: {
		// 打印日志
		debug: {type: Boolean,default: false},
		/**
		 * 是否转base64上传
		 * 差异
		 * 【App】true: file转base64上传，但返回给页面的还是原file，原因是跨浏览器传输base64太长会导致url不完整
		 * 【H5】true: file转base64上传并返回base64给页面
		 * 【微信小程序】true: file转base64返回页面，内置上传用的临时路径item.path(如果需要传base64，自行在页面用uni.request上传)
		 */
		toBase: {type: Boolean,default: false},
		htmlUrl: {type: String,default: 'https://oss.iyimeng.com/third_party_upload/hybrid/html/uploadFile.html'},
		// 是否去重文件（同名文件覆盖）
		distinct: {type: Boolean,default: false},
		// 自动上传
		instantly: {type: Boolean,default: true},
		// 上传接口参数设置
		option: {type: Object,default: ()=>{}},
		// 文件大小上限
		size: { type: Number, default: 10 },
		// 文件选择个数上限,超出后不触发点击
		count: { type: Number, default: 9 },
		// 是否允许多选文件
		multiple: {type:Boolean, default: true},
		// 允许上传的文件格式（多个以逗号隔开）
		formats: { type: String, default:''},
		// input file选择限制
		accept: {type: String,default: ''},
		// APP端权限校验 ---有效值查看https://ext.dcloud.net.cn/plugin?id=594
		permission: {
			type: Object,
			default:()=>{return {
				// ios一般无需自定义校验，如需要可像android一样配置，权限表地址https://ext.dcloud.net.cn/plugin?id=594
				'ios':[
					{
						value: 'camera',
						// toast标题
						title: '提示：', 
						// toast的message
						message: '文件选择器包含相机拍照上传，需要您授权相机权限', 
						// toast高度，100px适用于2行文字
						height: '100px' 
					},
					{
						value: 'record',
						// toast标题
						title: '提示：', 
						// toast的message
						message: '文件选择器包含音视频上传，需要您授权麦克风权限', 
						// toast高度，100px适用于2行文字
						height: '100px' 
					}
				],
				'android':[
					{
						value: 'android.permission.CAMERA',
						// toast标题
						title: '提示：', 
						// toast的message
						message: '文件选择器包含相机拍照上传，需要您授权相机权限', 
						// toast高度，100px适用于2行文字
						height: '100px' 
					},
					{
						value: 'android.permission.READ_EXTERNAL_STORAGE',
						title: '提示：',
						message: '文件选择需要您授权外部存储(含相册)读取权限',
						height: '100px'
					}
					// // 若需要可放开注释
					// ,
					// {
					// 	value: 'android.permission.RECORD_AUDIO',
					// 	message: '文件选择器包含录音上传，需要您授权麦克风权限'
					// }
				]
			}}
		},
		// 检测到未授权时顶部显示toast轻提示，内容为permission的message,,若传false不弹出内置Toast,
		// 可通过@permissionBefore回调自行处理，isPermissionInToast为false时才回调
		isPermissionInToast: {
			type: Boolean,
			default: true
		},
		// 用户未授权时显示弹框引导用户去系统开启权限,若传false不弹出内置Modal,
		// 可通过@permissionFail回调自行处理
		// 跳转系统界面表可查看 https://ask.dcloud.net.cn/question/14732
		isPermissionInModal: {
			type: Boolean,
			default: true
		},
		// 微信选择文件类型 
		//all=从所有文件选择，
		//video=只能选择视频文件，
		//image=只能选择图片文件，
		//file=可以选择除了图片和视频之外的其它的文件
		wxFileType: { type: String, default: 'all' },
		// webviewID需唯一，不同窗口也不要同Id
		childId: { type: String, default: 'lsjUpload'  },
		// 文件选择触发面宽度
		width: { type: String, default: '100%' },
		// 文件选择触发面高度
		height: { type: String, default: '80rpx' },
		// App端toast在页面中显示的位置：可选top、center、bottom
		verticalAlign: {type: String, default: 'center'},
		// top,left,bottom,right仅position=absolute时才需要传入
		top: { type: [String, Number], default: '' },
		left: { type: [String, Number], default: '' },
		bottom: { type: [String, Number], default: '' },
		right: { type: [String, Number], default: '' },
		// 是否区域滚动，主要用于在scroll内使用组件且滚动区域外还有其他内容情况
		// 传true时将监听组件是否滚动离开了scroll显示区域
		rangeScroll: {type: Boolean,default: false},
		// nvue不支持跟随窗口滚动
		position: { 
			type: String,
			// #ifdef APP-NVUE
			 default: 'absolute',
			// #endif
			// #ifndef APP-NVUE
			default: 'static',
			// #endif
		},
		// position:absolute时页面是否使用了系统导航栏
		isSystemNavigationBar: {
			type: Boolean,
			default: false
		}
	},
	data() {
		return {
			webId: '',
			statusBarH: 0
		}
	},
	computed: {
		getStyles() {
			let styles = {
				width: this.width,
				height: this.height
			}
			if (this.position == 'absolute') {
				styles['top'] = this.top
				styles['bottom'] = this.bottom
				styles['left'] = this.left
				styles['right'] = this.right
				styles['position'] = 'fixed'
			}

			return styles
		}
	},
	watch: {
		option(v) {
			// #ifdef APP-PLUS
			this.lsjFile&&this.show();
			// #endif
		}
	},
	updated() {
		// #ifdef APP-PLUS
			if (this.isShow) {
				this.lsjFile&&this.show();
			}
		// #endif
	},
	created() {
		uni.$on('$upload-show',this.emitShow);
		uni.$on('$upload-hide',this.hide);
	},
	beforeDestroy() {
		uni.$off('$upload-show',this.emitShow);
		uni.$off('$upload-hide',this.hide);
		if (observer) {
			observer.disconnect();
		}
		// #ifdef APP-PLUS
		this.lsjFile.dom.close();
		// #endif
	},
	mounted() {
		this.statusBarH = uni.getSystemInfoSync().statusBarHeight;
		let pages = getCurrentPages();
		this.myRoute = pages[pages.length - 1].route;
		this._size = 0;
		let WEBID = 'lsj_' + this.childId + this.getRandomString(10) + new Date().getTime();
		this.webId = WEBID;
		this.lsjFile = new LsjFile({
			id: WEBID,
			debug: this.debug,
			width: this.width,
			height: this.height,
			option: this.option,
			instantly: this.instantly,
			// 限制条件
			prohibited: {
				// 文件是否转base64
				toBase: this.toBase,
				// 是否去重
				distinct: this.distinct,
				// 大小
				size: this.size,
				// 允许上传的格式
				formats: this.formats,
				// 限制选择的格式
				accept: this.accept,
				count: this.count,
				// 是否多选
				multiple: this.multiple,
				// APP端Toast提示框位置 top、center、bottom
				verticalAlign: this.verticalAlign,
				permission: this.permission,
				isPermissionInToast: this.isPermissionInToast,
				isPermissionInModal: this.isPermissionInModal
			},
			onchange: this.onchange,
			onprogress: this.onprogress,
			permissionBefore: this.permissionBefore,
			permissionFail: this.permissionFail,
			permissionSuccess: this.permissionSuccess,
		});
		this.create();
		// #ifdef APP
		if (this.rangeScroll) {
			setTimeout(()=> {
				observer = uni.createIntersectionObserver(this);
				observer.relativeTo().observe('.hFile', (res) => {
				  if (res.intersectionRatio > 0 && !this.appear) {
					this.appear = true;
				  } else if (!res.intersectionRatio > 0 && this.appear) {
					this.appear = false;
					this.hideHandle();
				  }
				  this.$emit('observeChange',this.appear,res);
				})
			},200)
		}
		// #endif
	},
	methods: {
		getRandomString(length) {
		  var result = '';
		  var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
		  var charactersLength = characters.length;
		  for (var i = 0; i < length; i++) {
		    result += characters.charAt(Math.floor(Math.random() * charactersLength));
		  }
		  return result;
		},
		permissionBefore(res) {
			this.$emit('permissionBefore',res,this.childId);
		},
		permissionFail(res) {
			this.$emit('permissionFail',res,this.childId);
		},
    permissionSuccess(res) {
      this.$emit('permissionSuccess', res, this.childId);
    },
		setFiles(array) {
			this.lsjFile&&this.lsjFile.setFiles(array);
		},
		setData() {
			this.lsjFile&&this.lsjFile.setData(...arguments);
		},
		getDomStyles(callback) {
			// #ifndef APP-NVUE
			let view = uni
				.createSelectorQuery()
				.in(this)
				.select('.'+this.webId)
			view.fields(
				{
					size: true,
					rect: true
				},
				({ height, width, top, left, right, bottom }) => {
					if (this.position === 'absolute'&&this.isSystemNavigationBar) {
						top = top + 44 + (this.statusBarH || 0);
					}
					
					uni.createSelectorQuery()
					.selectViewport()
					.scrollOffset(({ scrollTop }) => {
						return callback({
							top: parseInt(top) + parseInt(scrollTop) + 'px',
							left: parseInt(left) + 'px',
							width: parseInt(width) + 'px',
							height: parseInt(height) + 'px'
						})
					})
					.exec()
				}
			).exec()
			// #endif
			// #ifdef APP-NVUE
			const dom = weex.requireModule('dom')
			dom.getComponentRect(this.$refs.lsj, ({ size: { height, width, top, left, right, bottom } }) => {
				if (this.position === 'absolute'&&this.isSystemNavigationBar) {
					top = top + 44 + (this.statusBarH || 0);
				}
				return callback({
					top: parseInt(top) + 'px',
					left: parseInt(left) + 'px',
					width: parseInt(width) + 'px',
					height: parseInt(height) + 'px',
					right: parseInt(right) + 'px',
					bottom: parseInt(bottom) + 'px'
				})
			})
			// #endif
		},
		emitShow() {
			let pages = getCurrentPages();
			let route = pages[pages.length - 1].route;
			if (route === this.myRoute) {
				return this.show();
			}
		},
		show() {
			this.debug&&console.log('触发show函数');
			if (this._size && (this._size >= this.count)) {
				return;
			}
			this.isShow = true;
			// #ifdef APP
			if (this.rangeScroll && !this.appear) {
				return;
			}
			// #endif
			this.showHandle();
		},
		hide() {
			this.debug&&console.log('触发hide函数');
			this.isShow = false;
			this.hideHandle();
		},
		showHandle() {
			// #ifdef APP-PLUS
			this.lsjFile&&this.getDomStyles(styles => {
				this.lsjFile.dom.setStyle(styles)
			});
			// #endif
			// #ifdef H5
			this.lsjFile.dom.style.display = 'inline'
			// #endif
		},
		hideHandle() {
			// #ifdef APP-PLUS
			this.lsjFile&&this.lsjFile.dom.setStyle({
				top: '-100px',
				left:'0px',
				width: '1px',
				height: '100px',
			});
			// #endif
			// #ifdef H5
			this.lsjFile.dom.style.display = 'none'
			// #endif
		},
		/**
		 * 手动提交上传
		 * @param {string}name 文件名称，不传则上传所有type等于waiting和fail的文件
		 */
		upload(name) {
			this.lsjFile&&this.lsjFile.upload(name);
		},
		/**
		 * @returns {Map} 已选择的文件Map集
		 */
		onchange(files) {
			this.$emit('changeFile',files,this.childId);
			this._size = files.size;
			return files.size >= this.count ? this.hide() : this.show();
		},
		/**
		 * @returns {object} 当前上传中的对象
		 */
		onprogress(item,end=false) {
			this.$emit('progress',item,this.childId);
			if (end) {
				setTimeout(()=>{
					this.$emit('uploadEnd',item,this.childId);
				},0);
			}
		},
		/**
		 * 移除组件内缓存的某条数据
		 * @param {string}name 文件名称,不指定默认清除所有文件
		 */
		clear(name) {
			this.lsjFile.clear(name);
		},
		// 创建选择器
		create() {
			// 若iOS端服务端处理不了跨域就将hybrid目录内的html放到服务端去，并将此处path改成服务器上的地址
			let path = this.htmlUrl;
			// let path = '/uni_modules/lsj-upload/hybrid/html/uploadFile.html';
			let dom = this.lsjFile.create(path);
			// #ifdef H5
			this.$refs.lsj.$el.appendChild(dom);
			// #endif
			// #ifndef APP-PLUS
			this.show();
			// #endif
			// #ifdef APP-PLUS
			dom.setStyle({position: this.position});
			dom.loadURL(path);
			setTimeout(()=>{
				try {
					// #ifdef APP-NVUE
					plus.webview.currentWebview().append(dom);
					// #endif
					// #ifndef APP-NVUE
					if (this.$root.$scope) {
						this.$root.$scope.$getAppWebview().append(dom);
					}
					else {
						getCurrentPages()[getCurrentPages().length - 1].$getAppWebview().append(dom);
					}
					// #endif
				} catch (error) {
					console.error(error);
				}
				this.show();
			},500)
			// #endif
		},
		// 点击选择附件
		onClick() {
			if (this._size >= this.count) {
				this.toast(`只允许上传${this.count}个文件`);
				return;
			}
			
			// #ifdef MP-WEIXIN
			if (!this.isShow) {return;}
			let count = this.count - this._size;
			this.lsjFile.chooseMessageFile(this.wxFileType,count);
			// #endif
		},
		toast(msg) {
			uni.showToast({
				title: msg,
				icon: 'none'
			});
		}
	}
}
</script>

<style scoped>
.lsj-file {
	display: inline-block;
}
.defview {
	background-color: #007aff;
	color: #fff;
	border-radius: 10rpx;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 28rpx;
}
.hFile {
	position: relative;
	overflow: hidden;
}
</style>
